/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.connection;

import java.util.ArrayList;
import java.util.Iterator;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.ControlFlow;
import org.eclipse.uml2.uml.Deployment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Extend;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.Manifestation;
import org.eclipse.uml2.uml.ObjectFlow;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Transition;

import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.pseudoelement.EnhancedType;
import com.cea.papyrus.core.pseudoelement.UMLElementFactory;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * 
 * 
 * @author CEA List
 */
public class ConnectionCreateCommand extends ConnectionCreateCommandBase implements IConnectionCreateCommand {
	
	/**
	 * 
	 */
	protected int channel=10; 
	
	/**
	 * 
	 */
	protected final Class<?> type;
	
	/**
	 * 
	 */
	protected final EnhancedType enhancedType;
	

	/**
	 * Instantiate a command that can create a connection between two Elements.
	 * 
	 * @param type of the link Dependency, azssociation...
	 * @param location 
	 * @param source the source endpoint
	 * @param point 	 */
	public ConnectionCreateCommand(GraphElement source, Object type, Point location) {
		super(source.getContainer(), source, location);

		Debug.debug(this,"Call New from graphElement :"+source.hashCode()+" "+getSourceElement().getClass()+" "+ type, channel);		
		if (source == null) {
			throw new IllegalArgumentException();
		}
		setLabel("connection creation");

		// Object is UMLElementFactory when the creation is initiated by 
		// a user defined button in palette
		if (type instanceof EnhancedType) {
			this.enhancedType = (EnhancedType) type;
			if (enhancedType instanceof UMLElementFactory) {
				this.type = ((UMLElementFactory) enhancedType).getClazz();
			} else {
				this.type = null;
			}

		} else {
			this.enhancedType = null;
			this.type         = (Class<?>) type;
		}
		
		Debug.debug(this,"type of the connection:"+type, channel);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.commands.Command#canExecute()
	 */
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.commands.IConnectionCreateCommand#canExecute()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public boolean canExecute() {
		Debug.debug(this,"canExecute ", channel);
		boolean canExecute = false;
		//umlElement
		Element umlSource=null;
		Element umlTarget=null;

		// Prevent from null graphical elements...
		if ((getSource() == null) || (getTarget() == null)) {
			return false;
		}	
		
		// Prevent from purely graphical elements...
		if ((getSourceElement() == null) || (getTargetElement() == null)) {
			return false;
		}
		
		umlSource=getSourceElement();
		umlTarget=getTargetElement();
		
		// Check for creation restrictions
		// These are given in enhancedType description
		boolean noRestriction = true;

		if ((enhancedType != null) && (enhancedType instanceof UMLElementFactory)) {
			noRestriction = ((UMLElementFactory) enhancedType).canExecute(getSource(), getTarget());
		}
		canExecute = ModelerHelper.getFactory().canConnect(type, umlSource, umlTarget);
		
		return (canExecute && noRestriction);
	}


	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.connections.commands.ConnectionCreateCommandBase#createGraphEdge(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlLink 
	 * 
	 * @return 
	 */
	@Override
	protected GraphEdge createGraphEdge(Element umlLink) {
		return  (GraphEdge)ModelerHelper.getFactory().createGraphElement(umlLink, getOwner(), null, null);
	}

	
	/**
	 * 
	 * 
	 * @param umlLink 
	 * 
	 * @return 
	 */
	protected boolean removeUmlLink(Element umlLink) {
		if (umlLink==null|| umlLink.getOwner()==null){return true;}
		return  ModelerHelper.getFactory().removeElement(umlLink.getClass().getInterfaces()[0], umlLink,umlLink.getOwner());
		}
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.connections.commands.ConnectionCreateCommandBase#createBussinessModel()
	 */
	/**
	 * Create the bussiness model.
	 * The created model should be stored by the implementation class for futur use (in reconnect/disconnect)
	 * 
	 * @return The created object.
	 */
	@Override
	public Element createBussinessModel() {
		// !! warning : the edge is still not created
		Element umlLink = null;
		
		//connect the graphEdge to the diagram and UML Elements
		final Element umlSource = getSourceElement();
		final Element umlTarget = getTargetElement();

		//((Notifier)getSource()).eAdapters().clear();
		//((Notifier)getTarget()).eAdapters().clear();
		//((Notifier)getOwner()).eAdapters().clear();

		// Create UML element(s)
		if ( type.equals(Generalization.class) ||
			 type.equals(Include.class)||
			 type.equals(Extend.class) ||
			 type.equals(Deployment.class) ||
			 type.equals(Manifestation.class)||
			 type.equals(ControlFlow.class) ||
			 type.equals(ObjectFlow.class) ){
			umlLink = ModelerHelper.getFactory().createUMLElement(type,getSourceElement());	
		}
		//must pass on a Region to contain the Transition
		else if(type.equals(Transition.class)){
			Element parElt = umlSource.getOwner();
			
			if(parElt instanceof State){
				if(((State)parElt).isComposite())
					parElt = ((State)parElt).getRegions().get(0);
				else				
					parElt = parElt.getOwner();
			}
			else if(parElt instanceof StateMachine){
				parElt = ((StateMachine)parElt).getRegions().get(0);
				
			}
			
			umlLink = ModelerHelper.getFactory().createUMLElement(type, parElt);

			if(getOwner() instanceof Diagram){
				ArrayList<GraphElement> found = new ArrayList<GraphElement>();
				LookForElement.lookForGraphElement(parElt, getOwner(), found);
				if(found.size() != 0)
					setOwner(found.get(0));
			}
		}
		else{
			umlLink = ModelerHelper.getFactory().createUMLElement(type,getUMLParent());
		}

	

		//connect UML element
		ModelerHelper.getFactory().connect(type, umlSource, umlTarget, umlLink);

		// Return the created bussiness element. It is stored inside the GraphEdge.
		return umlLink;
	}

	/**
	 * When creating an element which description is "enhancedType"
	 * this method is used to add required stereotype after creation.
	 * 
	 * @param child 
	 */
	protected void setStereotypes(Element child) {
		// Try to add stereotype if enhancedType is not null
		if ((enhancedType == null)
				|| (!(enhancedType instanceof UMLElementFactory)) ) {
			return;
		}

		UMLElementFactory enhancedUMLType = (UMLElementFactory) enhancedType;
		com.cea.papyrus.umlutils.Element childElt = new com.cea.papyrus.umlutils.Element(child);
		Iterator<String> stereotypeIt = enhancedUMLType.getStereotypes().iterator();

		while (stereotypeIt.hasNext()) {
			childElt.toggleStereotype(stereotypeIt.next(), true);
		}
	}

	/**
	 * When creating an element which description is "enhancedType"
	 * this method is used to select presentation just after creation.
	 * 
	 * @param child 
	 */
	protected void setPresentation(GraphEdge child) {
		// Try to set presentation if enhancedType is not null
		if ((enhancedType == null)
				|| (!(enhancedType instanceof UMLElementFactory)) ) {
			return;
		}

		UMLElementFactory enhancedUMLType = (UMLElementFactory) enhancedType;

		if (child.getSemanticModel() != null) {
			child.getSemanticModel().setPresentation(enhancedUMLType.getPresentation());
			
			//for each stereotype add a property to the node, used to display the stereotype
			Iterator<Stereotype> stereotypeList = ((Uml1SemanticModelBridge)child.getSemanticModel()).getElement().getAppliedStereotypes().iterator();
			while(stereotypeList.hasNext()){
				// property to add creation
				com.cea.papyrus.diagraminterchange2.di2.Property property = Di2Factory.eINSTANCE.createProperty();
				property.setKey(ModelerHelper.PROPERTY_STEREOTYPE_DISPLAY);
				property.setValue(((Stereotype)stereotypeList.next()).getQualifiedName());
	
				// add the property
				child.getProperty().add(property);
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.commands.IConnectionCreateCommand#setParent(com.cea.papyrus.diagraminterchange2.di2.GraphNode)
	 */
	/**
	 * 
	 * 
	 * @param parent 
	 */
	public void setParent(GraphNode parent){
		Debug.debug(this,"Call ConnectionCreateCommand.setParent to "+parent, channel);
		setOwner(parent);
	}


	/**
	 * 
	 * 
	 * @param bridge 
	 * @param targetBridge 
	 * @param sourceBridge 
	 */
	@Override
	public void disconnectBussinessModel(Uml1SemanticModelBridge bridge, Uml1SemanticModelBridge sourceBridge, Uml1SemanticModelBridge targetBridge) {
		ModelerHelper.getFactory().deconnect(type, getSourceElement(), getTargetElement(),umlLink);
		ModelerHelper.getFactory().removeElement(type, umlLink, getUMLParent());
	}

	/**
	 * 
	 * 
	 * @param bridge 
	 * @param targetBridge 
	 * @param sourceBridge 
	 */
	@Override
	public void reconnectBussinessModel(Uml1SemanticModelBridge bridge, Uml1SemanticModelBridge sourceBridge, Uml1SemanticModelBridge targetBridge) {
		// do nothing
		ModelerHelper.getFactory().addUMLElement(type, umlLink, getUMLParent());
		ModelerHelper.getFactory().connect(type,getSourceElement(), getTargetElement(),umlLink);

	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.connections.commands.ConnectionCreateCommandBase#updateBusinessModel()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public Object updateBusinessModel() {
		GraphEdge connection = getEdge();

		//((Notifier)connection).eAdapters().clear();

		// set Stereotypes and Presentation
		// Add stereotype and properties if needed...
		Uml1SemanticModelBridge umlBridge = (Uml1SemanticModelBridge) connection.getSemanticModel();
		if ((umlBridge != null) && (umlBridge.getElement() != null)) {

			Element connector = umlBridge.getElement();

			if ((enhancedType != null) 
					&& (enhancedType instanceof UMLElementFactory)
					&& (((UMLElementFactory) enhancedType).getStereotypes().size() > 0)) {
				//. setStereotype
				setStereotypes(connector);		
				//. setPresentation
				setPresentation(connection);
			}
		}
		return null;
	}
}
